package centroidtc;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;

public class CentroidBuilder {

	// a text parser
	private TextPaser pCorpus = null;
	// a inter-class document frequency map
	TreeMap<String, Integer> interDF = null;
	// a standard map template for all computing
	public TreeMap<String, Integer> word2DicTurn = null;

	/**
	 * constructor
	 */
	public CentroidBuilder() {
		pCorpus = new TextPaser();
		init();
	}

	/**
	 * init class
	 */
	private void init() {
		// get inter-class map
		String fileName = "out" + File.separator + "20-inter_df.txt";
		interDF = new TreeMap<String, Integer>();
		interDF.putAll(getMapFromFile(fileName));

		// get word's turn informatioin
		word2DicTurn = new TreeMap<String, Integer>();
		word2DicTurn.putAll(getStandardWordDic(interDF));
	}
	
	/**
	 * get text classification
	 */
	public void getTCInfo() {
		// prepare the writer
		FileManager trainWriter = new FileManager("centroid_train_sim.txt");
		FileManager testWriter = new FileManager("centroid_test_sim.txt");
		String fileName = "out" + File.separator + "20-newsgroup_vector.txt";
		// prepare the temp array
		ArrayList<Double> valueArray = new ArrayList<Double>();
		ArrayList<Integer> indexArray = new ArrayList<Integer>();
		ArrayList<Double> tArray = new ArrayList<Double>();
		
		// get the prototype vectors
		ArrayList<ArrayList<Double>> proto = this.getPrototypeVector();
		System.out.println("finish in getting all prototype vectors ...");
		
		BufferedReader in = null;
		try {
			in = new BufferedReader(new FileReader(fileName));
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}

		String s = null;
		int winnow = 0;
		try {
			while ((s = in.readLine()) != null) {
				// coping with a piece of record
				TreeMap<String, Double> aMap = getTextItem(s.trim());
				for (Map.Entry<String, Double> p : aMap.entrySet()) {
					// make the tfidf info into a sparse array
					valueArray.add(p.getValue());
					indexArray.add(word2DicTurn.get(p.getKey()));
				}
				aMap.clear();
				// text classification
				Double sim = 0.0;
				for (ArrayList<Double> centroid : proto) {
					for (int i = 0; i < valueArray.size(); i++) {
						// get the centroid's according index
						sim += valueArray.get(i) * centroid.get(indexArray.get(i));
					}
					tArray.add(sim);
					sim = 0.0;
				}
				// write out the result
				if ((winnow % 3) <= 1) {
					trainWriter.writeArrayIntoFile(normalizeArray(tArray), ",");//training set
				}
				if ((winnow % 3) > 1) {
					testWriter.writeArrayIntoFile(normalizeArray(tArray), ",");//testing set
				}
				
				tArray.clear();
				valueArray.clear();
				indexArray.clear();
				winnow++;
			}
			in.close();
			trainWriter.closeWriter();
			testWriter.closeWriter();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * parse a line of record into a text vector
	 */
	private TreeMap<String, Double> getTextItem(String s) {
		TreeMap<String, Double> ret = new TreeMap<String, Double>();
		String[] ta = s.split("::");
		// jump the document id
		String[] val = ta[1].split(",");
		for (int i = 0; i < val.length; i++) {
			String[] tv = val[i].split(":");
			ret.put(tv[0], Double.parseDouble(tv[1]));
		}
		return ret;
	}
	
	/**
	 * get a normalization to an Array
	 */
	public ArrayList<Double> normalizeArray(ArrayList<Double> arr) {
		ArrayList<Double> ret = null;
		if (arr == null) {
			return ret;
		}
		ret = new ArrayList<Double>();
		double sum = 0.0;
		double max = 0.0;
		for (Iterator<Double> iterator = arr.iterator(); iterator.hasNext();) {
			Double d = (Double) iterator.next();
			sum += d * d;
			if (max < d) {
				max = d;
			}
		}
//		double base = Math.sqrt(sum);
		for (Iterator<Double> iterator = arr.iterator(); iterator.hasNext();) {
			Double d = (Double) iterator.next();
			if (max == 0.0) {
				ret.add(0.0);
			} else {
				ret.add(d/max);
			}
			//ret.add(d/base);
		}
		
		return ret;
	}
	
	/**
	 * get prototype vectors
	 */
	public ArrayList<ArrayList<Double>> getPrototypeVector() {
		ArrayList<ArrayList<Double>> ret = new ArrayList<ArrayList<Double>>();
		TreeMap<String, Double> aMap = new TreeMap<String, Double>();
		double ci = 0.0;
		int catNum = pCorpus.subClassItems.size();
		for (int i = 0; i < pCorpus.subClassItems.size(); i++) {
			// get a cetegory's file list
			ArrayList<String> fList = parseLongString(pCorpus.subClassItems
					.get(i).getDocId());
			// get this category's inner document frequency
			TreeMap<String, Integer> tm = this.getProtoItem(i);
			// get the category's positive discriminative index
			for (Map.Entry<String, Integer> p : tm.entrySet()) {
				String tmpWord = (String) p.getKey();
				double inter_class_df = this.interDF.get(tmpWord) + 0.0;
				double inner_class_df = tm.get(tmpWord);
				double normalValue = fList.size() + 0.0;
				double numClass = catNum + 0.0;
//				double base = 10.0;
//				double base = Math.E - 1.0;
				double base = Math.E - 1.7;//1.7
//				double base = Math.E - 1.5;
//				double base = Math.E - 1.2;
//				ci = Math.log10(numClass / inter_class_df);
				ci = Math.log(numClass / inter_class_df);
//				ci = Math.pow(base, (inter_class_df / numClass));
//				ci = Math.pow(base, (inner_class_df / normalValue));
				ci = ci * Math.pow(base, (inner_class_df / normalValue));
				aMap.put(tmpWord, (ci > 0.0 ? ci : 0.0));
				ci = 0.0;
			}
			// standard this prototype vector
			ArrayList<Double> tmpA = new ArrayList<Double>();
			for (Map.Entry<String, Integer> p : word2DicTurn.entrySet()) {
				if (aMap.containsKey((String) p.getKey())) {
					tmpA.add(aMap.get(p.getKey()));
				} else {
					tmpA.add(0.0);
				}
			}
			ret.add(tmpA);
			aMap.clear();
			tm.clear();
		}
		return ret;
	}
	
	/**
	 * parse a "word#" style's string info
	 */
	public ArrayList<String> parseLongString(String aBigStr) {
		ArrayList<String> ret = null;
		if (aBigStr == null) {
			return ret;
		}
		ret = new ArrayList<String>();
		String[] tmp = aBigStr.split(",");
		for (int i = 0; i < tmp.length; i++) {
			if (!tmp[i].equalsIgnoreCase("")) {
				ret.add(tmp[i]);
			}
		}
		return ret;
	}

	/**
	 * change a line of record into a text vector
	 */
	private TreeMap<String, Integer> getProtoItem(int catNum) {
		TreeMap<String, Integer> ret = new TreeMap<String, Integer>();
		String fileName = "out" + File.separator + "20-inner_df.txt";
		BufferedReader in = null;
		try {
			in = new BufferedReader(new FileReader(fileName));
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		int index = 0;
		String s = null;
		try {
			while ((s = in.readLine()) != null) {
				String[] ta = s.trim().split(",");
				if (index == catNum) {
					for (int i = 0; i < ta.length; i++) {
						String[] p = ta[i].split(":");
						ret.put(p[0], Integer.parseInt(p[1]));
					}
				}
				index++;
			}
			in.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return ret;
	}

	/**
	 * get a map from a file
	 */
	private TreeMap<String, Integer> getMapFromFile(String aFileName) {
		TreeMap<String, Integer> ret = null;
		if (aFileName == null) {
			return ret;
		}

		ret = new TreeMap<String, Integer>();
		BufferedReader in = null;
		try {
			in = new BufferedReader(new FileReader(aFileName));
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}

		String s = null;
		String[] ta = null;
		try {
			while ((s = in.readLine()) != null) {
				String ts = s.trim();
				ta = ts.split(":");
				ret.put(ta[0], Integer.parseInt(ta[1]));
			}
			in.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return ret;
	}

	/**
	 * get the standard word dictionary
	 */
	public TreeMap<String, Integer> getStandardWordDic(
			TreeMap<String, Integer> dicMap) {
		TreeMap<String, Integer> ret = new TreeMap<String, Integer>();
		int index = 0;
		for (Iterator<Map.Entry<String, Integer>> iter1 = dicMap.entrySet()
				.iterator(); iter1.hasNext();) {
			Map.Entry<String, Integer> p = (Map.Entry<String, Integer>) iter1
					.next();
			ret.put((String) p.getKey(), index);
			index += 1;
		}
		return ret;
	}
	
	public static void main(String[] args) {

		CentroidBuilder cb = new CentroidBuilder();

		cb.getTCInfo();
		System.out.println("lexicon's length:" + cb.word2DicTurn.size());
		// System.out.println("text's number:"+cb.textId2CorpusIndex.size());
		// System.out.println("train set:"+cb.trainSet.size());
		// System.out.println("test set:"+cb.testSet.size());
	}
}
